﻿using System;
using System.Linq;

using Microsoft.WowAddonStudio.Lua.AST;
using Microsoft.WowAddonStudio.Lua.Model;

using NUnit.Framework;

using LuaParser = Microsoft.WowAddonStudio.Lua.Parser.Parser;
using LuaScanner = Microsoft.WowAddonStudio.Lua.Parser.Scanner;

namespace Microsoft.WowAddonStudio.Lua.Tests.Parser
{
    /// <summary>
    /// Contains tests for the basic <see cref="LuaParser"/>.
    /// </summary>
    [TestFixture]
    public class ParserTest : ParserTestBase<LuaParser>
    {
        [Test]
        public void ChunkTest()
        {
            // Set the source to an empty Lua file
            parser.SetSource(String.Empty);

            // Trigger parsing and assert result
            this.AssertParse();

            // Assert block and empty statement list
            Assert.IsNotNull(parser.Chunk.Block);
            Assert.IsNull(parser.Chunk.Block.StatementList);
        }

        [Test]
        public void LastStatementReturnNoExpressionTest()
        {
            // Set the source containing a 'return' statement as a last statement
            parser.SetSource("return");

            // Trigger parsing
            this.AssertParse();

            // Assert result
            Assert.IsNotNull(parser.Chunk.Block);
            Assert.IsNotNull(parser.Chunk.Block.StatementList);
            Assert.IsTrue(parser.Chunk.Block.StatementList is Return);
            Assert.IsNull(((Return)parser.Chunk.Block.StatementList).ExpressionList);
        }

        [Test]
        public void LastStatementReturnNilTest()
        {
            // Set the source containing a 'return' statement as a last statement
            parser.SetSource("return nil");

            // Trigger parsing
            this.AssertParse();

            // Assert result
            Assert.IsNotNull(parser.Chunk.Block);
            Assert.IsNotNull(parser.Chunk.Block.StatementList);

            // Assert return statement
            var returnStatement = parser.Chunk.Block.StatementList as Return;

            Assert.IsNotNull(returnStatement);
            Assert.IsNotNull(returnStatement.ExpressionList);
            Assert.IsTrue(returnStatement.ExpressionList is Literal);
            Assert.IsTrue(((Literal)returnStatement.ExpressionList).Type == LuaType.Nil);
        }

        [Test]
        public void LiteralValueTest()
        {
            // Set the source containing literal values as a single assignment expression
            parser.SetSource("x = nil, true, 42, 'hello'");

            // Trigger parsing
            this.AssertParse();

            // Assert result
            Assert.IsNotNull(parser.Chunk.Block);
            Assert.IsNotNull(parser.Chunk.Block.StatementList);

            // Assert assignment statement
            var asssignmentStatement = parser.Chunk.Block.StatementList as Assignment;

            Assert.IsNotNull(asssignmentStatement);

            // Assert the expressions
            var expressions = asssignmentStatement.ExpressionList.OfType<Literal>().ToArray();

            // Assert the expression types
            Assert.AreEqual(4, expressions.Length);
            Assert.AreEqual(LuaType.Nil, expressions[0].Type);
            Assert.AreEqual(LuaType.Boolean, expressions[1].Type);
            Assert.AreEqual(LuaType.Number, expressions[2].Type);
            Assert.AreEqual(LuaType.String, expressions[3].Type);

            // Assert the expression values
            Assert.AreEqual("nil", expressions[0].Value);
            Assert.AreEqual("true", expressions[1].Value);
            Assert.AreEqual("42", expressions[2].Value);
            Assert.AreEqual("'hello'", expressions[3].Value);
        }

        [Test]
        public void EllipsisTest()
        {
            // Set the source containing an assignment that uses ellipsis
            parser.SetSource("a,b,c = ...");

            // Trigger parsing
            this.AssertParse();

            // Assert result
            Assert.IsNotNull(parser.Chunk.Block);
            Assert.IsNotNull(parser.Chunk.Block.StatementList);

            // Assert assignment statement
            var asssignmentStatement = parser.Chunk.Block.StatementList as Assignment;

            Assert.IsNotNull(asssignmentStatement);
            Assert.IsTrue(asssignmentStatement.ExpressionList is Ellipsis);

            var assignments = asssignmentStatement.GetSingleAssignments().GetEnumerator();

            int count = 0;

            while (assignments.MoveNext())
            {
                Assert.IsNotNull(assignments.Current.Key);
                Assert.IsTrue(assignments.Current.Value is Ellipsis);

                count++;
            }

            Assert.AreEqual(3, count);
        }

        [Test]
        public void GetNodeAtTest()
        {
            // Set the source to a method call
            parser.SetSource("Frame1:Show();");

            // Assert the parse
            this.AssertParse();

            // Get the node at the position
            var node = parser.GetNodeAt(0, 1);

            // Assert the node
            Assert.IsNotNull(node);
            Assert.IsTrue(node is Identifier);
        }
    }
}